/*
 *    Copyright (c) 2014 Xiang Xu <xuxiang@mail.bnu.edu.cn>
 *
 *    Permission is hereby granted, free of charge, to any person
 *    obtaining a copy of this software and associated documentation
 *    files (the "Software"), to deal in the Software without
 *    restriction, including without limitation the rights to use,
 *    copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the
 *    Software is furnished to do so, subject to the following
 *    conditions:
 *
 *    The above copyright notice and this permission notice shall be
 *    included in all copies or substantial portions of the Software.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
 */

#include "arguments.h"
#include "facade_grammar.h"
#include "facade_model.h"

#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/centroid.h>
#include <pcl/common/common.h>
#include "boost/program_options.hpp"
#include "boost/smart_ptr.hpp"
#include "opencv2/opencv.hpp"

#include <fstream>
using namespace std;

// query this after executing a function
Status status;

// organize variables
boost::shared_ptr<Arguments> arguments = boost::shared_ptr<Arguments>(new Arguments);

boost::shared_ptr<FacadeGrammar> facade_grammar;
boost::shared_ptr<FacadeModel> facade_model;

boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;
pcl::PointCloud<pcl::PointXYZ>::Ptr build_cloud(new pcl::PointCloud<pcl::PointXYZ>);
double** grid;

void keyboardEventOccurred(const pcl::visualization::KeyboardEvent &event, void* cookie);
void AddParsingResult();
void MyAddCube2(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax,
                double r, double g, double b, double transparent, double outline_transparent,
                int line_width, std::string id);

struct ResultSymbol
{
    std::string name_;
    int pos_x_, pos_y_, width_, height_;
};

std::vector<ResultSymbol> result_symbols;
int symbol_number;

double original_x, original_y;
double resolution;
int width, height;
double zmin = -3.0;
double zmax = 3.0;

void AddSymbols();
void MyAddCube(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax,
               double r, double g, double b, double transparent, double outline_transparent,
               int line_width, std::string id);
void ConstructBuildBox();
void DebugLearningResult(std::string filename);

int outline_red = 0;
int outline_green = 0;
int outline_blue = 0;
double outline_transparent = 1.0;

double br = 0.8;
double bg = 0.8;
double bb = 0.8;
double bt = 1.0;

#define RENDER_3D 0

int main(int argc, char** argv)
{
    Status status;

    // parse parameters
    status = arguments->Parse(argc, argv);

    if(!status.IsOk())
    {
        cout << status;
        exit(-1);
    }
    else
    {
        cout << "Argments Parsed success.\n";
    }

    // Step 1
    cout << "\nLoad the shape grammar.\n";
    facade_grammar = boost::shared_ptr<FacadeGrammar>(new FacadeGrammar);
    status = facade_grammar->ReadGrammar(arguments->GetGrammarFile());
    if (!status.IsOk())
	 {
        cout << status;
        exit(-1);
    }

    // Step 2
    cout << "\nInitialize the facade model, ";
    facade_model = boost::shared_ptr<FacadeModel>(new FacadeModel(facade_grammar));

    // Step 3
    cout << "\nLoad grids.\n";
    facade_model->LoadGrids(arguments->GetGridFile());

    // Load the symbols
    ifstream ifs(arguments->GetResultAsciiFile().c_str());

    if(!ifs.is_open())
    {
        return -1;
    }

    double score;
    ifs >> score;
    ifs >> symbol_number;
    ifs >> original_x >> original_y >> resolution >> width >> height;
    result_symbols.reserve(symbol_number);

    for(int i = 0; i < symbol_number; ++i)
    {
        ResultSymbol symbol;
        ifs >> symbol.name_ >> symbol.pos_x_ >> symbol.pos_y_ >> symbol.width_ >> symbol.height_;
        result_symbols.push_back(symbol);
    }

    ifs.close();

    assert(width == facade_model->get_width());
    assert(height == facade_model->get_height());
    //
    grid = facade_model->get_grd_ptr(2);
    DebugLearningResult(arguments->GetResultValidateFile());

    // Open the viewer and add the symbols to the viewer
    viewer.reset(new pcl::visualization::PCLVisualizer());

#if !RENDER_3D
    // load the building cloud.
    pcl::io::loadPCDFile(arguments->GetFacadeTransformedFile(), *build_cloud);

    pcl::PointXYZ min_extent, max_extent;
    pcl::getMinMax3D(*build_cloud, min_extent, max_extent);

    std::cout << "x diff: " << min_extent.x - original_x << std::endl;
std:
    cout << "y diff: " << min_extent.y - original_y << std::endl;
    std::cout << "width diff: " << (max_extent.x - min_extent.x) / resolution - width << std::endl;
    std::cout << "height diff: " << (max_extent.y - min_extent.y) / resolution - height << std::endl;
    viewer->addPointCloud<pcl::PointXYZ> (build_cloud, "build plane cloud");

    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 0.0, 0.0, "build plane cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "build plane cloud");

#endif

    //viewer->setBackgroundColor(1.0, 1.0, 1.0);
    viewer->setBackgroundColor(210.0 / 250.0, 71.0 / 250.0, 38.0 / 250.0);
    viewer->addCoordinateSystem();

#if RENDER_3D
    AddSymbols();
#else
    AddParsingResult();
#endif

    viewer->registerKeyboardCallback(&keyboardEventOccurred);
    viewer->spin();
}

void DebugLearningResult(std::string filename)
{
    cv::Mat mat(height, width, CV_8UC3);
    cv::Mat mat_learning(height, width, CV_8UC3);
    cv::Mat mat_compare(height, width, CV_8UC3);

    cv::Mat_<cv::Vec3b> _Mat = mat;

    for(int i = 0; i < height; ++i)
    {
        for(int j = 0; j < width; ++j)
        {
            _Mat(i, j)[0] = (uchar)(grid[i][j] * 255);
            _Mat(i, j)[1] = (uchar)(grid[i][j] * 255);
            _Mat(i, j)[2] = (uchar)(grid[i][j] * 255);
        }
    }

    for(int i = 0; i < symbol_number; ++i)
    {
        ResultSymbol symbol = result_symbols[i];

        float x_min = symbol.pos_x_;
        float x_max = x_min + symbol.width_;
        float y_min = symbol.pos_y_;
        float y_max = y_min + symbol.height_;

        if(symbol.name_ == "wall")
        {
            cv::rectangle(mat_learning, cv::Point(x_min, y_min), cv::Point(x_max, y_max),
                          cv::Scalar(0, 255, 255), -1, 8);
        }
        else
        {
            cv::rectangle(mat_learning, cv::Point(x_min, y_min), cv::Point(x_max, y_max),
                          cv::Scalar(0, 0, 255), -1, 8);
        }
    }

    cv::addWeighted(_Mat, 0.5, mat_learning, 0.5, 0.0, mat_compare);

    imwrite(filename, mat_compare);
}

void AddParsingResult()
{
    int count = 0;
    double transparent = 0.5;
    int line_width = 3;

    outline_red = 1.0;
    outline_green = 1.0;
    outline_blue = 0.0;
    outline_transparent = 1.0;

    zmax = 1;

    for(int i = 0; i < symbol_number; ++i)
    {
        ResultSymbol symbol = result_symbols[i];

        float x_min = original_x + symbol.pos_x_ * resolution;
        float x_max = x_min + symbol.width_ * resolution;
        float y_min = original_y + symbol.pos_y_ * resolution;
        float y_max = y_min + symbol.height_ * resolution;

        count++;
        ostringstream id;
        id << symbol.name_ << i;
        std::vector<double> symbol_color = facade_grammar->get_symbol_color(symbol.name_);

        MyAddCube2(x_min, x_max, y_min, y_max, zmin, zmax,
                   symbol_color[0], symbol_color[1], symbol_color[2], transparent, outline_transparent, line_width, id.str());
    }
}

void AddSymbols()
{
    int count = 0;
    double transparent = 0.8;
    //double outline_transparent = 0.8;
    int line_width = 2;

    for(int i = 0; i < symbol_number; ++i)
    {
        ResultSymbol symbol = result_symbols[i];

        float x_min = original_x + symbol.pos_x_ * resolution;
        float x_max = x_min + symbol.width_ * resolution;
        float y_min = original_y + symbol.pos_y_ * resolution;
        float y_max = y_min + symbol.height_ * resolution;

        count++;
        ostringstream id;
        id << symbol.name_ << i;
        std::vector<double> symbol_color = facade_grammar->get_symbol_color(symbol.name_);

#if RENDER_3D

        if(symbol.name_ == "wall")
        {
            zmin = 0;
            zmax = 3;

            symbol_color[0] = 0.8;
            symbol_color[1] = 0.8;
            symbol_color[2] = 0.8;

        }

        if(symbol.name_ == "window")
        {
            zmin = 0;
            zmax = 2.5;
        }

        if(symbol.name_ == "balcony")
        {
            zmin = 0;
            zmax = 4;
        }

        if(symbol.name_ == "pillar")
        {
            zmin = 0;
            zmax = 3.7;
        }

        if(symbol.name_ == "roof")
        {
            zmin = 0;
            zmax = 3;
        }

        if(symbol.name_ == "door")
        {
            zmin = 0;
            zmax = 2.3;
        }

        if(symbol.name_ == "hole")
        {
            zmin = 0;
            zmax = 2.0;
        }

        transparent = 1.0;
        outline_transparent = 1.0;
#else
        zmax = 0;
#endif

        MyAddCube(x_min, x_max, y_min, y_max, zmin, zmax,
                  symbol_color[0], symbol_color[1], symbol_color[2], transparent, outline_transparent, line_width, id.str());
        //if ( symbol.name_ == "window" )
        //{
        //	id << "window " << count;
        //	MyAddCube(x_min, x_max, y_min, y_max, zmin, zmax, 0.0, 0.0, 1.0, transparent, id.str());
        //}
        //else // Wall
        //{
        //	if(symbol.width_ == width)
        //	{
        //		// flat wall
        //		id << "flat wall " << count;
        //		MyAddCube(x_min, x_max, y_min, y_max, zmin, zmax, 0.0, 1.0, 0.0, transparent, id.str());
        //	}
        //	else
        //	{
        //		// floor wall
        //		id << "wall " << count;
        //		MyAddCube(x_min, x_max, y_min, y_max, zmin, zmax, 0.0, 1.0, 1.0, transparent, id.str());
        //	}
        //}
    }

    zmin = 0;
    zmax = 3;
    ConstructBuildBox();
}

void MyAddCube2(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax,
                double r, double g, double b, double transparent, double outline_transparent,
                int line_width, std::string id)
{
    pcl::PointXYZ p1(xmin, ymin, zmin), p2(xmin, ymax, zmin), p3(xmax, ymax, zmin),
        p4(xmax, ymin, zmin), p5(xmin, ymin, zmax), p6(xmin, ymax, zmax), p7(xmax, ymax, zmax), p8(xmax, ymin, zmax);

    pcl::PointCloud<pcl::PointXYZ>::Ptr facade1(new pcl::PointCloud<pcl::PointXYZ>);


    facade1->width = 4;
    facade1->height = 1;

    // front
    facade1->push_back(p5);
    facade1->push_back(p6);
    facade1->push_back(p7);
    facade1->push_back(p8);

    Eigen::Vector4f planarCoeff;
    planarCoeff(0) = 0.0;
    planarCoeff(1) = 0.0;
    planarCoeff(2) = 1.0;
    planarCoeff(3) = 0.0;
    pcl::PlanarPolygon<pcl::PointXYZ> facade1_planar(facade1->points, planarCoeff);

    std::string facade_name = id + "_front";
    viewer->addPolygon(facade1_planar, r, g, b, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, transparent, facade_name);

    facade_name = id + "_front_outline";
    viewer->addPolygon(facade1_planar, outline_red, outline_green, outline_blue, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, line_width, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, outline_transparent, facade_name);
}


void MyAddCube(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax,
               double r, double g, double b, double transparent, double outline_transparent,
               int line_width, std::string id)
{
    pcl::PointXYZ p1(xmin, ymin, zmin), p2(xmin, ymax, zmin), p3(xmax, ymax, zmin),
        p4(xmax, ymin, zmin), p5(xmin, ymin, zmax), p6(xmin, ymax, zmax), p7(xmax, ymax, zmax), p8(xmax, ymin, zmax);

    pcl::PointCloud<pcl::PointXYZ>::Ptr facade1(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr facade2(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr facade3(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr facade4(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr facade5(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr facade6(new pcl::PointCloud<pcl::PointXYZ>);

    facade1->width = 4;
    facade1->height = 1;

    facade2->width = 4;
    facade2->height = 1;

    facade3->width = 4;
    facade3->height = 1;

    facade4->width = 4;
    facade4->height = 1;

    facade5->width = 4;
    facade5->height = 1;

    facade6->width = 4;
    facade6->height = 1;

    // front
    facade1->push_back(p1);
    facade1->push_back(p2);
    facade1->push_back(p3);
    facade1->push_back(p4);

    // back
    facade2->push_back(p5);
    facade2->push_back(p6);
    facade2->push_back(p7);
    facade2->push_back(p8);

    // left
    facade3->push_back(p1);
    facade3->push_back(p5);
    facade3->push_back(p6);
    facade3->push_back(p2);

    // right
    facade4->push_back(p3);
    facade4->push_back(p4);
    facade4->push_back(p8);
    facade4->push_back(p7);

    // top
    facade5->push_back(p2);
    facade5->push_back(p3);
    facade5->push_back(p7);
    facade5->push_back(p6);

    // bottom
    facade6->push_back(p1);
    facade6->push_back(p4);
    facade6->push_back(p8);
    facade6->push_back(p5);


    Eigen::Vector4f planarCoeff;
    planarCoeff(0) = 0.0;
    planarCoeff(1) = 0.0;
    planarCoeff(2) = 1.0;
    planarCoeff(3) = 0.0;
    pcl::PlanarPolygon<pcl::PointXYZ> facade1_planar(facade1->points, planarCoeff);
    pcl::PlanarPolygon<pcl::PointXYZ> facade2_planar(facade2->points, planarCoeff);
    pcl::PlanarPolygon<pcl::PointXYZ> facade3_planar(facade3->points, planarCoeff);
    pcl::PlanarPolygon<pcl::PointXYZ> facade4_planar(facade4->points, planarCoeff);
    pcl::PlanarPolygon<pcl::PointXYZ> facade5_planar(facade5->points, planarCoeff);
    pcl::PlanarPolygon<pcl::PointXYZ> facade6_planar(facade6->points, planarCoeff);

    std::string facade_name = id + "_front";
    viewer->addPolygon(facade1_planar, br, bg, bb, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, bt, facade_name);

    facade_name = id + "_front_outline";
    viewer->addPolygon(facade1_planar, outline_red, outline_green, outline_blue, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, line_width, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, outline_transparent, facade_name);

    facade_name = id + "_back";
    viewer->addPolygon(facade2_planar, r, g, b, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, transparent, facade_name);

    facade_name = id + "_back_outline";
    viewer->addPolygon(facade2_planar, outline_red, outline_green, outline_blue, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, line_width, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, outline_transparent, facade_name);

    //
    facade_name = id + "_left";
    viewer->addPolygon(facade3_planar, br, bg, bb, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, bt, facade_name);

    facade_name = id + "_left_outline";
    viewer->addPolygon(facade3_planar, outline_red, outline_green, outline_blue, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, line_width, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, outline_transparent, facade_name);

    //
    facade_name = id + "_right";
    viewer->addPolygon(facade4_planar, br, bg, bb, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, bt, facade_name);

    facade_name = id + "_right_outline";
    viewer->addPolygon(facade4_planar, outline_red, outline_green, outline_blue, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, line_width, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, outline_transparent, facade_name);

    //
    facade_name = id + "_top";
    viewer->addPolygon(facade5_planar, br, bg, bb, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, bt, facade_name);

    facade_name = id + "_top_outline";
    viewer->addPolygon(facade5_planar, outline_red, outline_green, outline_blue, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, line_width, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, outline_transparent, facade_name);

    //
    facade_name = id + "_bottom";
    viewer->addPolygon(facade6_planar, br, bg, bb, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, bt, facade_name);

    facade_name = id + "_bottom_outline";
    viewer->addPolygon(facade6_planar, outline_red, outline_green, outline_blue, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, line_width, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, outline_transparent, facade_name);
}

void ConstructBuildBox()
{
    float x_min = original_x;
    float x_max = x_min + width * resolution;
    float y_min = original_y;
    float y_max = y_min + height * resolution;
    pcl::PointXYZ p1(x_min, y_min, zmin), p2(x_min, y_max, zmin), p3(x_max, y_max, zmin),
        p4(x_max, y_min, zmin), p5(x_min, y_min, zmax), p6(x_min, y_max, zmax),
        p7(x_max, y_max, zmax), p8(x_max, y_min, zmax);

    pcl::PointCloud<pcl::PointXYZ>::Ptr facade3(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr facade4(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr facade5(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr facade6(new pcl::PointCloud<pcl::PointXYZ>);

    facade3->width = 4;
    facade3->height = 1;
    facade4->width = 4;
    facade4->height = 1;
    facade5->width = 4;
    facade5->height = 1;
    facade6->width = 4;
    facade6->height = 1;

    // left
    facade3->push_back(p1);
    facade3->push_back(p2);
    facade3->push_back(p6);
    facade3->push_back(p5);

    // right
    facade4->push_back(p3);
    facade4->push_back(p4);
    facade4->push_back(p8);
    facade4->push_back(p7);

    // top
    facade5->push_back(p2);
    facade5->push_back(p6);
    facade5->push_back(p7);
    facade5->push_back(p3);

    // bottom
    facade6->push_back(p1);
    facade6->push_back(p5);
    facade6->push_back(p8);
    facade6->push_back(p4);

    Eigen::Vector4f planarCoeff;
    planarCoeff(0) = 0.0;
    planarCoeff(1) = 0.0;
    planarCoeff(2) = 1.0;
    planarCoeff(3) = 0.0;
    pcl::PlanarPolygon<pcl::PointXYZ> facade3_planar(facade3->points, planarCoeff);
    pcl::PlanarPolygon<pcl::PointXYZ> facade4_planar(facade4->points, planarCoeff);
    pcl::PlanarPolygon<pcl::PointXYZ> facade5_planar(facade5->points, planarCoeff);
    pcl::PlanarPolygon<pcl::PointXYZ> facade6_planar(facade6->points, planarCoeff);

    double r = 0.5;
    double g = 0.5;
    double b = 0.5;
    double transparent = 1.0;//0.5;

    std::string facade_name = "left facade";
    viewer->addPolygon(facade3_planar, r, g, b, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, transparent, facade_name);

    facade_name = "right facade";
    viewer->addPolygon(facade4_planar, r, g, b, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, transparent, facade_name);

    facade_name = "top facade";
    viewer->addPolygon(facade5_planar, r, g, b, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, transparent, facade_name);

    facade_name = "bottom facade";
    viewer->addPolygon(facade6_planar, r, g, b, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
                                        pcl::visualization::PCL_VISUALIZER_REPRESENTATION_SURFACE, facade_name);
    viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, transparent, facade_name);
}

void keyboardEventOccurred(const pcl::visualization::KeyboardEvent &event, void* cookie)
{
    if(event.getKeySym() == "k")
    {
        viewer->removeAllPointClouds();
    }

    if(event.getKeySym() == "a")
        viewer->addPointCloud<pcl::PointXYZ> (build_cloud, "build plane cloud");

    if(event.getKeySym() == "K")
        viewer->removeAllShapes();

    if(event.getKeySym() == "A")
        AddSymbols();

    if(event.getKeySym() == "m")
        AddParsingResult();
}
